#ifndef NE_STL_INCLUDENE_STL_INUSE_VALUE_H
#define NE_STL_INCLUDENE_STL_INUSE_VALUE_H

#include <optional>
#include "ne_stl_build.h"
#include "ne_stl_type_traits.h"

namespace nstd {

template <class T>
struct _optional_base {
    bool has_value_{false};
    T value_;
};
template <class T>
struct _optional : _optional_base<T> {
public:
    _optional() = default;
    _optional(const _optional& other) {
        has_value_ = other.has_value_;
        if (has_value_) {
            value_ = other.value_;
        }
    }
    _optional(const T& value) {
        has_value_ = true;
        value_ = value;
    }
    _optional(std::nullptr_t) {}
    _optional(std::nullopt_t) {}
    template <class TValue>
    _optional(const std::optional<TValue>& other) {
        has_value_ = other.has_value();
        if (has_value_) {
            value_ = static_cast<T>(other.value());
        }
    }

    template <class TValue>
    operator std::optional<TValue>() const {
        if (!has_value_) {
            return std::nullopt;
        }
        return std::optional<TValue>(static_cast<TValue>(value_));
    }

    template <class TValue>
    operator _optional<TValue>() const {
        if (!has_value_) {
            return std::nullopt;
        }
        return _optional<TValue>(static_cast<TValue>(value_));
    }

    template <class TValue>
    _optional(const TValue* other) {
        has_value_ = true;
        value_ = *other;
    }
    template <class TValue>
    _optional& operator=(const TValue* value) {
        has_value_ = true;
        value_ = *value;
        return *this;
    }
    _optional& operator=(const char* value) {
        has_value_ = true;
        value_ = value;
        return *this;
    }
    _optional& operator=(std::nullptr_t) {
        has_value_ = false;
        return *this;
    }
    _optional& operator=(std::nullopt_t) {
        has_value_ = false;
        return *this;
    }
    template <class TValue>
    _optional& operator=(const std::optional<TValue>& other) {
        has_value_ = other.has_value();
        if (has_value_) {
            value_ = static_cast<T>(other.value());
        }
        return *this;
    }

    _optional& operator=(const T& value) {
        has_value_ = true;
        value_ = value;
        return *this;
    }
    _optional& operator=(const _optional& other) {
        has_value_ = other.has_value_;
        value_ = other.value_;
        return *this;
    }
    template <class TValue>
    _optional& operator=(const TValue& value) {
        has_value_ = true;
        value_ = value;
        return *this;
    }
    template <class TValue>
    _optional& operator=(const _optional<TValue>& other) {
        has_value_ = other.has_value();
        value_ = other.value();
        return *this;
    }
    bool operator==(const _optional& other) const {
        if (has_value_ != other.has_value_) {
            return false;
        }
        if (!has_value_) {
            return true;
        }
        return value_ == other.value_;
    }
    bool operator!=(const _optional& other) const { return !(*this == other); }
    void reset() { has_value_ = false; }
    const T& value() const& { return value_; }
    T value_or(T&& default_value) const& { return has_value_ ? value_ : default_value; }
    bool has_value() const { return has_value_; }
    operator bool() const { return has_value_; }
    const T* operator->() const { return &value_; }
    T* operator->() { return &value_; }
    const T& operator*() const& { return value_; }
    T& operator*() & { return value_; }
    const T&& operator*() const&& { return value_; }
    T&& operator*() && { return value_; }
    using _base_t = _optional_base<T>;
    using _base_t::has_value_;
    using _base_t::value_;
};
template <class T>
using optional = _optional<T>;

inline constexpr std::nullopt_t nullopt = std::nullopt;

}  // namespace nstd

#endif  // !NE_STL_INCLUDENE_STL_INUSE_VALUE_H
